#ifndef __POINT3__
#define __POINT3__

#include <exception>
#include "Point4.h"

#ifndef byte
typedef unsigned char byte;
#endif

template<typename T>
struct Matrix4;

template<typename T>
struct Point3
{
public:
	static const Point3 Zero;

	T X, Y, Z;
	
	/// <summary>
	/// Initializes a new instance of the <see cref="Point3&lt;T&gt;" /> struct, assigning all values to zero.
	/// </summary>
	Point3() :
		X(0),
		Y(0),
		Z(0)
	{
	};

	/// <summary>
	/// Initializes a new instance of the <see cref="Point3&lt;T&gt;" /> struct.
	/// </summary>
	/// <param name="x">The x.</param>
	/// <param name="y">The y.</param>
	/// <param name="z">The z.</param>
	Point3(const T &x, const T &y, const T &z) :
		X(x),
		Y(y),
		Z(z)
	{
	};
	
	/// <summary>
	/// Initializes a new instance of the <see cref="Point3&lt;T&gt;" /> struct.
	/// </summary>
	/// <param name="p">The point to copy values from.</param>
	Point3(const Point3 &p) :
		X(p.X),
		Y(p.Y),
		Z(p.Z)
	{
	};

	template<typename PT>
	Point3(const Point4<PT> &p) :
		X(p.X),
		Y(p.Y),
		Z(p.Z)
	{
	};

	T &operator[](const T &index)
	{
		switch (index)
		{
		case 0:
			return X;
		case 1:
			return Y;
		case 2:
			return Z;
		}

		throw std::exception();
	}

	inline bool operator ==(const Point3 &r) const
	{
		return X == r.X && Y == r.Y && Z == r.Z;
	}

	inline bool operator !=(const Point3 &r) const
	{
		return !operator==(r);
	}
	
	inline Point3 operator+(const Point3 &r) const
	{
		return Point3(X + r.X, Y + r.Y, Z + r.Z);
	}

	inline Point3 operator-(const Point3 &r) const
	{
		return Point3(X - r.X, Y - r.Y, Z - r.Z);
	}

	inline Point3 &operator+=(const Point3 &r)
	{
		X += r.X;
		Y += r.Y;
		Z += r.Z;

		return *this;
	}
	
	inline Point3 operator*(const float &r) const
	{
		return Point3(X * r, Y * r, Z * r);
	}
	
	inline Point3 operator/(const float &r) const
	{
		return Point3(X / r, Y / r, Z / r);
	}
	
	inline bool operator< (const Point3 &r) const
	{ 
		if (X < r.X)
			return true;
        
		if (X == r.X) 
        { 
            if (Y < r.Y)
				return true;
            if (Y == r.Y)
				return Z < r.Z;
        }

        return false;
	}

	inline bool operator<= (const Point3 &r) const
	{ 
		return operator==(r) || operator<(r);
	}

	inline bool operator> (const Point3 &r) const
	{
		return !operator<=(r);
	}

	float Dot(const Point3 &right) const
	{
		return (((X * right.X) + (Y * right.Y)) + (Z * right.Z));
	}

	float GetLength()
	{
		return sqrtf((X * X) + (Y * Y) + (Z * Z));
	}

	void Normalize()
	{
		float num = 1 / GetLength();
		X *= num;
		Y *= num;
		Z *= num;
	}

	void AngleVectors(Point3 *forward, Point3 *right, Point3 *up)
	{
		float sr, sp, sy, cr, cp, cy, t;

		sincosf(MathHelper::DegreesToRadians(Y), sy, cy);
		sincosf(MathHelper::DegreesToRadians(X), sp, cp);

		if (forward)
		{
			forward->X = cp * cy;
			forward->Y = cp * sy;
			forward->Z = -sp;
		}

		if (right || up)
			sincosf(MathHelper::DegreesToRadians(Z), sr, cr);

		if (right)
		{
			t = sr*sp;
			right->X = -t*cy+cr*sy;
			right->Y = -t*sy-cr*cy;
			right->Z = -sr*cp;
		}

		if (up)
		{
			t = cr*sp;
			up->X = t*cy+sr*sy;
			up->Y = t*sy+sr*cy;
			up->Z = cr*cp;
		}
	}
};

template<typename T>
inline Point3<T> operator*(const float &left, const Point3<T> &right)
{
	return Point3<T>(left * right.X, left * right.Y, left * right.Z);
}

template<typename T>
const Point3<T> Point3<T>::Zero(0, 0, 0);

typedef Point3<byte> Point3b;
typedef Point3<int> Point3i;
typedef Point3<float> Point3f;
#endif